package streamdef;

import java.io.BufferedReader;
import java.io.StringReader;
import java.text.ParseException;
import java.util.Vector;
import streamdef.DEFINES.FieldRelationType;
import event.EventFormatManager;
import event.DEFINES;

public class StreamDefinitionParser {
	private static StreamDefinitionParser sInstance = null;

	public static StreamDefinitionParser instance() {
		if (sInstance == null)
			sInstance = new StreamDefinitionParser();

		return sInstance;
	}

	private Vector<String> primitiveStreams = null;
	private Vector<String> complexStreams = null;

	private StreamDefinitionParser() {
		init();
	}

	public void reset() {
		if (primitiveStreams != null)
			primitiveStreams.clear();
		if (complexStreams != null)
			complexStreams.clear();
	}

	protected void finalize() {
		reset();
	}

	public void init() {
		reset();
		primitiveStreams = new Vector<String>();
		complexStreams = new Vector<String>();
	}

	public void parse(String query) throws ParseException {
		query = query.toLowerCase(); // make it case-insensitive
		if (query.startsWith("create pstream") == true)
			parsePrimitive(query);
		else if (query.startsWith("create cstream") == true)
			parseComplex(query);
		else
			throw new java.text.ParseException("Check the create-syntax!", -1);
	}

	// create pstream -> one line only, defining the ID and the data fields
	// -> defining the corresponding complex event format
	private void parsePrimitive(String query) throws ParseException {
		EventFormatManager efm = EventFormatManager.instance();
		long streamID = -1;
		Vector<DEFINES.CEPLEventDataFieldDefinition> format = new Vector<DEFINES.CEPLEventDataFieldDefinition>();

		String ori = new String(query);
		query = query.trim().replaceAll(" +", " ");

		if (query.endsWith(System.getProperty("line.separator")))
			query = query.substring(0,
					query.lastIndexOf(System.getProperty("line.separator")));
		if (query.contains(System.getProperty("line.separator")))
			throw new java.text.ParseException(
					"pstream :: Only one line of text allowed!", -1);

		if (query.indexOf('(') == -1 || query.indexOf('(') + 1 > query.length()
				|| query.lastIndexOf(')') == -1
				|| query.indexOf('(') > query.lastIndexOf(')'))
			throw new java.text.ParseException(
					"pstream :: Check the brackets!", -1);
		query = query.substring(query.indexOf('(') + 1, query.lastIndexOf(')'));

		String delims = ",";
		String delims2 = ":";
		String[] tokens = query.split(delims);
		for (int i = 0; i < tokens.length; ++i) {
			if (i > 0) {
				String[] tokens2 = tokens[i].split(delims2);
				if (tokens2.length < 2 || tokens2.length > 3)
					throw new java.text.ParseException(
							"pstream :: Check the field definitions!", -1);

				DEFINES.CEPLEventDataFieldDefinition def = new DEFINES.CEPLEventDataFieldDefinition();
				tokens2[0] = tokens2[0].trim();
				tokens2[1] = tokens2[1].trim();
				def.fieldName = tokens2[0].toLowerCase();

				if (tokens2[1].compareTo("char") == 0) {
					def.isFloatingPoint = false;
					def.isNumerical = true;
					def.isPrecededByLengthInBytes = false;
					def.lengthInBytes = 1;

					if (tokens2.length > 2)
						throw new java.text.ParseException(
								"pstream :: Check the field definitions!", -1);
				} else if (tokens2[1].compareTo("short") == 0) {
					def.isFloatingPoint = false;
					def.isNumerical = true;
					def.isPrecededByLengthInBytes = false;
					def.lengthInBytes = 2;

					if (tokens2.length > 2)
						throw new java.text.ParseException(
								"pstream :: Check the field definitions!", -1);
				} else if (tokens2[1].compareTo("int") == 0) {
					def.isFloatingPoint = false;
					def.isNumerical = true;
					def.isPrecededByLengthInBytes = false;
					def.lengthInBytes = 4;

					if (tokens2.length > 2)
						throw new java.text.ParseException(
								"pstream :: Check the field definitions!", -1);
				} else if (tokens2[1].compareTo("long") == 0) {
					def.isFloatingPoint = false;
					def.isNumerical = true;
					def.isPrecededByLengthInBytes = false;
					def.lengthInBytes = 8;

					if (tokens2.length > 2)
						throw new java.text.ParseException(
								"pstream :: Check the field definitions!", -1);
				} else if (tokens2[1].compareTo("float") == 0) {
					def.isFloatingPoint = true;
					def.isNumerical = true;
					def.isPrecededByLengthInBytes = false;
					def.lengthInBytes = 4;

					if (tokens2.length > 2)
						throw new java.text.ParseException(
								"pstream :: Check the field definitions!", -1);
				} else if (tokens2[1].compareTo("double") == 0) {
					def.isFloatingPoint = true;
					def.isNumerical = true;
					def.isPrecededByLengthInBytes = false;
					def.lengthInBytes = 8;

					if (tokens2.length > 2)
						throw new java.text.ParseException(
								"pstream :: Check the field definitions!", -1);
				} else if (tokens2[1].compareTo("text") == 0) {
					def.isFloatingPoint = false;
					def.isNumerical = false;
					Short s = null;

					if (tokens2.length != 3)
						throw new java.text.ParseException(
								"pstream :: Check the field definitions!", -1);
					tokens2[2] = tokens2[2].trim();
					try {
						s = Short.parseShort(tokens2[2]);
					} catch (NumberFormatException e) {
						throw new java.text.ParseException(
								"pstream :: Check the field definitions!", -1);
					}

					if (s.shortValue() <= 0) {
						def.isPrecededByLengthInBytes = true;
						def.lengthInBytes = 0;
					} else {
						def.isPrecededByLengthInBytes = false;
						def.lengthInBytes = s;
					}

				} else
					throw new java.text.ParseException(
							"pstream :: Check the field types!", -1);

				format.add(def);
			} else {
				try {
					streamID = Long.parseLong(tokens[i]);
				} catch (NumberFormatException e) {
					throw new java.text.ParseException(
							"pstream :: Check the generator ID!", -1);
				}
			}
		}

		efm.addEventFormat(streamID, format);
		primitiveStreams.add(ori);
	}

	// create cstream -> multiple lines possible
	// first line is for defining the ID and the data fields -> format
	private void parseComplex(String query) throws ParseException {
		EventFormatManager efm = EventFormatManager.instance();
		StreamDefinitionManager pqm = StreamDefinitionManager.instance();

		Vector<DEFINES.CEPLEventDataFieldDefinition> format = new Vector<DEFINES.CEPLEventDataFieldDefinition>();
		StreamCondition streamCondition = new StreamCondition();

		String ori = new String(query);
		query = query.trim().replaceAll(" +", " ");
		BufferedReader reader = new BufferedReader(new StringReader(query));
		String line = null, lineFROM = null, lineCREATE = null;
		int lineNum = 0;

		try {
			while ((line = reader.readLine()) != null) {
				lineNum++;
				if (lineNum == 1) // create cstream...
					lineCREATE = new String(line);
				else if (lineNum == 2) { // from internalID1:externalID1[range]
					lineFROM = new String(line);
					parseComplexEventUsedStreamsAndRanges(lineFROM,
							streamCondition);
					parseComplexEventFormat(lineCREATE, streamCondition, format);
				} else { // WHERE AND AND ...
					parseComplexEventSubQuery(line, streamCondition);
				}
			}
		} catch (Exception e1) {
			e1.printStackTrace();
		}

		streamCondition.calculateEventSequences();
		streamCondition.calculateForbiddenEventsNotInStatements();
		efm.addEventFormat(streamCondition.getStreamID(), format);
		pqm.addParsedQuery(streamCondition);
		complexStreams.add(ori);
	}

	private void parseComplexEventUsedStreamsAndRanges(String query,
			StreamCondition streamCondition) throws ParseException {
		query = query.trim();
		if (!query.startsWith("from "))
			throw new java.text.ParseException(
					"cstream :: Check the FROM-statement!", -1);
		query = query.substring(query.indexOf(' ') + 1);
		query = query.replaceAll(" ", "");
		String delims = ",";

		// split the string into streams
		String[] tokens = query.split(delims);
		for (int i = 0; i < tokens.length; ++i) {
			boolean mayOccur = true;
			long internalID, externalID;
			double range = -1;

			tokens[i] = tokens[i].trim();

			//
			// RANGE is defined
			//
			if (tokens[i].indexOf('[') > 0) {
				if (tokens[i].lastIndexOf('[') != tokens[i].indexOf('[')
						|| tokens[i].lastIndexOf(']') == -1
						|| tokens[i].lastIndexOf(']') != tokens[i].indexOf(']'))
					throw new ParseException(
							"cstream :: Check the FROM-statement! Missing brackets?",
							-1);
				else {
					String rangeString = tokens[i].substring(
							tokens[i].indexOf('[') + 1,
							tokens[i].lastIndexOf(']'));
					tokens[i] = tokens[i].substring(0, tokens[i].indexOf('['));

					rangeString.trim();
					if (rangeString.indexOf(' ') != -1)
						throw new ParseException(
								"cstream :: Check the FROM-statement! Spaces within the brackets?",
								-1);
					else {
						if (rangeString.startsWith("!")) {
							mayOccur = false;
							rangeString = rangeString.substring(1);
						}
						try {
							range = Double.parseDouble(rangeString);
						} catch (NumberFormatException e) {
							throw new ParseException(
									"cstream :: Check the FROM-statement! Invalid RANGE-value?",
									-1);
						}
					}
				}
			}

			//
			// internalID:externalID
			//

			String delims2 = ":";
			String[] tokens2 = tokens[i].split(delims2);
			if (tokens2.length != 2)
				throw new ParseException(
						"cstream :: Check the FROM-statement!", -1);
			tokens2[0] = tokens2[0].trim();
			tokens2[1] = tokens2[1].trim();

			try {
				internalID = Long.parseLong(tokens2[0]);
				externalID = Long.parseLong(tokens2[1]);
			} catch (NumberFormatException e) {
				throw new java.text.ParseException(
						"cstream :: Check the FROM-statement! Invalid numbers?",
						-1);
			}

			if (EventFormatManager.instance().isValidStreamID(externalID) == false)
				throw new java.text.ParseException(
						"cstream :: Check the FROM-statement! Unknown external stream ID?",
						-1);
			else
				streamCondition.addInternalStreamID(internalID, externalID,
						range, mayOccur);
		}
	}

	// the FROM-statement is already parsed
	// <format> will be filled with the parsed data fields
	private void parseComplexEventFormat(String query,
			StreamCondition streamCondition,
			Vector<DEFINES.CEPLEventDataFieldDefinition> format)
			throws java.text.ParseException {
		if (query.indexOf('(') == -1 || query.indexOf('(') + 1 > query.length()
				|| query.lastIndexOf(')') == -1
				|| query.indexOf('(') > query.lastIndexOf(')'))
			throw new java.text.ParseException(
					"cstream :: Check the brackets!", -1);
		query = query.substring(query.indexOf('(') + 1, query.lastIndexOf(')'));

		long streamID = -1;

		String delims = ","; // ...,...,...
		String delims2 = ":"; // fieldName:id1.field1
		String delims3 = "\\."; // id1.field1

		// split the string into field definitions
		String[] tokens = query.split(delims);
		for (int i = 0; i < tokens.length; ++i) {
			String tok = tokens[i].trim();

			//
			// first token == ID, others are field definitions
			//
			if (i == 0) {
				try {
					streamID = Long.parseLong(tokens[i]);
				} catch (NumberFormatException e) {
					throw new java.text.ParseException(
							"cstream :: Check the generator ID!", -1);
				}

				if (EventFormatManager.instance().isValidStreamID(streamID))
					throw new java.text.ParseException(
							"cstream :: Check the generator ID! It already exists?",
							-1);
				else
					streamCondition.setStreamID(streamID);
			} else {
				//
				// split into fieldName and field origin
				//
				String[] tokens2 = tok.split(delims2);
				if (tokens2.length != 2)
					throw new java.text.ParseException(
							"cstream :: Check the field definitions!", -1);
				tokens2[0] = tokens2[0].trim();
				tokens2[1] = tokens2[1].trim();

				String newFieldName = new String(tokens2[0]);

				String[] tokens3 = (new String(tokens2[1])).split(delims3);
				if (tokens3.length != 2)
					throw new java.text.ParseException(
							"cstream :: Check the field definitions!", -1);
				tokens3[0] = tokens3[0].trim();
				tokens3[1] = tokens3[1].trim();

				long origInternalStreamID = -1;
				try {
					origInternalStreamID = Long.parseLong(tokens3[0]);
				} catch (NumberFormatException e) {
					throw new java.text.ParseException(
							"cstream :: Check the stream ID's!", -1);
				}

				// FROM-statement is already parsed
				Long origExternalStreamID = streamCondition
						.getExternalStreamID(origInternalStreamID);
				if (origExternalStreamID == null)
					throw new java.text.ParseException(
							"cstream :: Check the stream ID's! The internal stream ID is not defined?",
							-1);
				else if (!streamCondition.mayOccur(origInternalStreamID))
					throw new java.text.ParseException(
							"cstream :: Check the stream ID's! Non-occurring event in a new event definition?",
							-1);
				else {
					String origFieldName = new String(tokens3[1]);
					event.DEFINES.CEPLEventDataFieldType type = EventFormatManager
							.instance().getFieldValueType(origExternalStreamID,
									origFieldName);

					if (type == event.DEFINES.CEPLEventDataFieldType.UNKNOWN)
						throw new java.text.ParseException(
								"cstream :: Unknown data field ("
										+ origFieldName + ")!", -1);

					DEFINES.CEPLEventDataFieldDefinition def = new DEFINES.CEPLEventDataFieldDefinition();
					def.fieldName = newFieldName.toLowerCase();

					switch (type) {
					case CHAR:
						def.isFloatingPoint = false;
						def.isNumerical = true;
						def.isPrecededByLengthInBytes = false;
						def.lengthInBytes = 1;
						break;
					case SHORT:
						def.isFloatingPoint = false;
						def.isNumerical = true;
						def.isPrecededByLengthInBytes = false;
						def.lengthInBytes = 2;
						break;
					case INT:
						def.isFloatingPoint = false;
						def.isNumerical = true;
						def.isPrecededByLengthInBytes = false;
						def.lengthInBytes = 4;
						break;
					case LONG:
						def.isFloatingPoint = false;
						def.isNumerical = true;
						def.isPrecededByLengthInBytes = false;
						def.lengthInBytes = 8;
						break;
					case FLOAT:
						def.isFloatingPoint = true;
						def.isNumerical = true;
						def.isPrecededByLengthInBytes = false;
						def.lengthInBytes = 4;
						break;
					case DOUBLE:
						def.isFloatingPoint = true;
						def.isNumerical = true;
						def.isPrecededByLengthInBytes = false;
						def.lengthInBytes = 8;
						break;
					case TEXT:
						def.isFloatingPoint = false;
						def.isNumerical = false;
						short lengthInBytes = EventFormatManager.instance()
								.getLengthInBytes(origExternalStreamID,
										origFieldName);

						if (lengthInBytes == 0) {
							def.isPrecededByLengthInBytes = true;
							def.lengthInBytes = 0;
						} else {
							def.isPrecededByLengthInBytes = false;
							def.lengthInBytes = lengthInBytes;
						}

						break;
					}
					format.add(def);
					streamCondition.addField(newFieldName,
							origInternalStreamID, origFieldName);
				}
			}
		}

		if (streamCondition.getStreamID() < 0) {
			throw new java.text.ParseException(
					"cstream :: Check the generator ID!", -1);
		}
	}

	private class EncapsulatedLong {
		Long value = null;
	}

	private void parseComplexEventSubQuery(String query,
			StreamCondition streamCondition) throws java.text.ParseException {
		query = query.trim();
		SubCondition subCondition = new SubCondition();

		EncapsulatedLong id1 = new EncapsulatedLong();
		id1.value = new Long(-1);
		EncapsulatedLong id2 = new EncapsulatedLong();
		id2.value = new Long(-1);

		// not the first line, but also no AND -> syntax error
		// the first line, but AND -> syntax error
		if (streamCondition.numberOfSubQueries() > 0
				&& !query.startsWith("and "))
			throw new java.text.ParseException(
					"cstream :: Check the substatements! Missing AND?", -1);
		else if (streamCondition.numberOfSubQueries() == 0
				&& !(query.startsWith("where ")))
			throw new java.text.ParseException(
					"cstream :: Check the substatements! No WHERE in the first line?",
					-1);

		// remove leading WHERE/AND
		if (query.startsWith("where "))
			query = query.substring(6);
		else if (query.startsWith("and "))
			query = query.substring(4);

		// the subquery is also an AND- or OR-statement, within brackets
		if (query.indexOf('(') != -1) {
			if (query.lastIndexOf(')') == -1) // no closing
				throw new java.text.ParseException(
						"cstream :: Check the substatements! Missing bracket?",
						-1);

			String temp = query.substring(query.indexOf('(') + 1,
					query.lastIndexOf(')'));
			temp = temp.trim();

			if (!(temp.contains(" and ") || temp.contains(" or ")))
				throw new java.text.ParseException(
						"cstream :: Check the substatements! Unnecessary brackets?",
						-1);
			if (temp.contains(" and ") && temp.contains(" or "))
				throw new java.text.ParseException(
						"cstream :: Check the substatements! Mixture of AND and OR within brackets?",
						-1);

			// set the AND/OR-status of the subquery
			if (temp.contains(" and ")) {
				subCondition.setANDStatement(true);
				temp = temp.replace(" and ", ":");
				temp = temp.replace(")", "");
				temp = temp.replace("(", "");
			} else {
				subCondition.setANDStatement(false);
				temp = temp.replace(" or ", ":");
				temp = temp.replace(")", "");
				temp = temp.replace("(", "");
			}

			String delims = ":";
			// split the subquery into separate statements
			String[] tokens = temp.split(delims);
			for (int i = 0; i < tokens.length; ++i)
				parseComplexEventSubQueryStatement(tokens[i].trim(),
						subCondition, streamCondition, id1, id2);
		}
		// no brackets -> only AND in front of the subquery
		else {
			query = query.trim();

			if (query.replace(" or ", "").compareToIgnoreCase(query) != 0
					|| query.replace(" and ", "").compareToIgnoreCase(query) != 0)
				throw new java.text.ParseException(
						"cstream :: Check the substatements! Missing brackets?",
						-1);
			else
				parseComplexEventSubQueryStatement(query, subCondition,
						streamCondition, id1, id2);
		}

		streamCondition.addSubQuery(subCondition);
	}

	private void parseComplexEventSubQueryStatement(String statement,
			SubCondition subCondition, StreamCondition streamCondition,
			EncapsulatedLong id1ToCheckWith, EncapsulatedLong id2ToCheckWith)
			throws java.text.ParseException {
		// time relation -> using the timestamp data field
		if (statement.contains(" before ")) {
			if (subCondition.AndOrTypeIsSet() && !subCondition.isANDStatement())
				throw new java.text.ParseException(
						"cstream :: Check the substatements! BEFORE-statement in an OR-sequence ?!",
						-1);

			statement = statement.replace(" before ", ":");
			String delims = ":";
			String[] tokens = statement.split(delims);
			if (tokens.length != 2)
				throw new java.text.ParseException(
						"cstream :: Check the substatements! Wrong BEFORE-statement?",
						-1);
			try {
				long id1 = Long.parseLong(tokens[0].trim());
				long id2 = Long.parseLong(tokens[1].trim());

				if (id1 == id2)
					throw new java.text.ParseException(
							"cstream :: Check the substatements! Two different streams needed !",
							-1);
				else if ((id1ToCheckWith.value == id1 && id2ToCheckWith.value == id2)
						|| (id1ToCheckWith.value == id2 && id2ToCheckWith.value == id1))
					;
				else if (id1ToCheckWith.value != -1
						&& id2ToCheckWith.value != -1) {
					throw new java.text.ParseException(
							"cstream :: Check the substatements! More than two streams in one line ?",
							-1);
				} else if (id1ToCheckWith.value == -1
						&& id2ToCheckWith.value == -1) {
					id1ToCheckWith.value = id1;
					id2ToCheckWith.value = id2;
				} else if (id1ToCheckWith.value != -1) {
					if (id1ToCheckWith.value == id1)
						id2ToCheckWith.value = id2;
					else if (id1ToCheckWith.value == id2)
						id2ToCheckWith.value = id1;
					else
						throw new java.text.ParseException(
								"cstream :: Check the substatements! More than two streams in one line ?",
								-1);
				} else if (id2ToCheckWith.value != -1) {
					if (id2ToCheckWith.value == id1)
						id1ToCheckWith.value = id2;
					else if (id2ToCheckWith.value == id2)
						id1ToCheckWith.value = id1;
					else
						throw new java.text.ParseException(
								"cstream :: Check the substatements! More than two streams in one line ?",
								-1);
				}

				createNewTimeRelationTwoStreams(id1, id2, subCondition,
						streamCondition);
			} catch (NumberFormatException e) {
				throw new java.text.ParseException(
						"cstream :: Check the substatements! Wrong BEFORE-statement?",
						-1);
			}
		}
		// value relation -> get the relation (=, <, >,...)
		else {
			FieldRelationType rel = FieldRelationType.UNKNOWN;
			if (statement.contains(">=")) {
				statement = statement.replace(">=", ":");
				rel = FieldRelationType.EQUALSORGREATER;
			} else if (statement.contains("<=")) {
				statement = statement.replace("<=", ":");
				rel = FieldRelationType.EQUALSORLESSER;
			} else if (statement.contains("==")) {
				statement = statement.replace("==", ":");
				rel = FieldRelationType.EQUALS;
			} else if (statement.contains("!=")) {
				statement = statement.replace("!=", ":");
				rel = FieldRelationType.NOTEQUALS;
			} else if (statement.contains("<")) {
				statement = statement.replace("<", ":");
				rel = FieldRelationType.LESSER;
			} else if (statement.contains(">")) {
				statement = statement.replace(">", ":");
				rel = FieldRelationType.GREATER;
			} else
				throw new java.text.ParseException(
						"cstream :: Check the substatements! Missing comparison sign?",
						-1);
			String delims = ":";
			String[] tokens = statement.split(delims);
			if (tokens.length != 2)
				throw new java.text.ParseException(
						"cstream :: Check the substatements! Wrong equality-statement?",
						-1);
			//
			// left part == ID1.field1
			//
			if (!tokens[0].contains("."))
				throw new java.text.ParseException(
						"cstream :: Check the substatements! Wrong equality-statement?",
						-1);

			String delims2 = "\\.";
			String[] tokens2 = tokens[0].split(delims2);
			if (tokens2.length != 2)
				throw new java.text.ParseException(
						"cstream :: Check the substatements! Wrong equality-statement?",
						-1);

			long origStreamID1 = -1; // ID1
			try {
				origStreamID1 = Long.parseLong(tokens2[0].trim());
			} catch (NumberFormatException e) {
				throw new java.text.ParseException(
						"cstream :: Check the substatements! Wrong stream ID?",
						-1);
			}
			String origFieldName1 = tokens2[1].trim(); // field1

			// check if the right part is a plain value
			boolean isValue = tokens[1].contains("'"); // for a string
			if (!isValue) {
				isValue = !tokens[1].contains("."); // for a short/long/int/char
			}
			if (!isValue) {
				try {
					Double.parseDouble(tokens[1]); // for a double/float
					isValue = true;
				} catch (NumberFormatException e) {
					isValue = false;
				}
			}

			//
			// right part == ID2.field2
			//
			if (!isValue) {
				String delims3 = "\\.";
				String[] tokens3 = tokens[1].split(delims3);
				if (tokens3.length != 2)
					throw new java.text.ParseException(
							"cstream :: Check the substatements! Wrong equality-statement?",
							-1);

				long origStreamID2 = -1; // ID2
				try {
					origStreamID2 = Long.parseLong(tokens3[0].trim());
				} catch (NumberFormatException e) {
					throw new java.text.ParseException(
							"cstream :: Check the substatements! Wrong stream ID?",
							-1);
				}
				String origFieldName2 = tokens3[1].trim(); // field2

				if (origStreamID1 == origStreamID2)
					throw new java.text.ParseException(
							"cstream :: Check the substatements! Two different streams needed !",
							-1);
				else if ((id1ToCheckWith.value == origStreamID1 && id2ToCheckWith.value == origStreamID2)
						|| (id1ToCheckWith.value == origStreamID2 && id2ToCheckWith.value == origStreamID1))
					;
				else if (id1ToCheckWith.value != -1
						&& id2ToCheckWith.value != -1) {
					throw new java.text.ParseException(
							"cstream :: Check the substatements! More than two streams in one line ?",
							-1);
				} else if (id1ToCheckWith.value == -1
						&& id2ToCheckWith.value == -1) {

					id1ToCheckWith.value = origStreamID1;
					id2ToCheckWith.value = origStreamID2;
				} else if (id1ToCheckWith.value != -1) {
					if (id1ToCheckWith.value == origStreamID1)
						id2ToCheckWith.value = origStreamID2;
					else if (id1ToCheckWith.value == origStreamID2)
						id2ToCheckWith.value = origStreamID1;
					else
						throw new java.text.ParseException(
								"cstream :: Check the substatements! More than two streams in one line ?",
								-1);
				} else if (id2ToCheckWith.value != -1) {
					if (id2ToCheckWith.value == origStreamID1)
						id1ToCheckWith.value = origStreamID2;
					else if (id2ToCheckWith.value == origStreamID2)
						id1ToCheckWith.value = origStreamID1;
					else
						throw new java.text.ParseException(
								"cstream :: Check the substatements! More than two streams in one line ?",
								-1);
				}

				createNewFieldFromTwoStreams(origStreamID1,
						origFieldName1.trim(), rel, origStreamID2,
						origFieldName2.trim(), subCondition, streamCondition);
			}
			//
			// right part == value
			//
			else {
				if (id1ToCheckWith.value == origStreamID1
						|| id2ToCheckWith.value == origStreamID1)
					;
				else if (id1ToCheckWith.value == -1)
					id1ToCheckWith.value = origStreamID1;
				else if (id2ToCheckWith.value == -1)
					id2ToCheckWith.value = origStreamID1;
				else
					throw new java.text.ParseException(
							"cstream :: Check the substatements! More than two streams in one line ?",
							-1);

				createNewFieldFromOneStream(origStreamID1,
						origFieldName1.trim(), rel, tokens[1].trim(),
						subCondition, streamCondition);

			}
		}
	}

	private void createNewTimeRelationTwoStreams(long internalID1,
			long internalID2, SubCondition subCondition,
			StreamCondition streamCondition) throws java.text.ParseException {
		if (streamCondition.getExternalStreamID(internalID1) == null
				|| streamCondition.getExternalStreamID(internalID2) == null)
			throw new java.text.ParseException(
					"cstream :: Check the substatements! Unknown stream ID?",
					-1);
		else {
			ConditionStatementTimeRelationTwoStreams newStat = new ConditionStatementTimeRelationTwoStreams(
					internalID1, internalID2);
			subCondition.addQueryStatement(newStat);
		}
	}

	private void createNewFieldFromOneStream(long internalID, String fieldName,
			FieldRelationType rel, String value, SubCondition subCondition,
			StreamCondition streamCondition) throws java.text.ParseException {
		if (streamCondition.getExternalStreamID(internalID) == null)
			throw new java.text.ParseException(
					"cstream :: Check the substatements! Unknown stream ID?",
					-1);

		Long externalID = streamCondition.getExternalStreamID(internalID);
		event.DEFINES.CEPLEventDataFieldType type = EventFormatManager
				.instance().getFieldValueType(externalID, fieldName);

		if (type == event.DEFINES.CEPLEventDataFieldType.UNKNOWN)
			throw new java.text.ParseException(
					"cstream :: Check the substatements! Unknown data field?",
					-1);

		boolean ok = true;
		ConditionStatementFieldFromOneStream result = null;

		// check if it's the correct type
		if (type == event.DEFINES.CEPLEventDataFieldType.CHAR) {
			if (value.length() != 1)
				ok = false;
			else
				result = new ConditionStatementFieldFromOneStream(internalID,
						fieldName, new Character(value.charAt(0)), rel);
		} else if (type == event.DEFINES.CEPLEventDataFieldType.DOUBLE) {
			try {
				double d = Double.parseDouble(value);
				result = new ConditionStatementFieldFromOneStream(internalID,
						fieldName, new Double(d), rel);
			} catch (NumberFormatException e) {
				ok = false;
			}
		} else if (type == event.DEFINES.CEPLEventDataFieldType.FLOAT) {
			try {
				float f = Float.parseFloat(value);
				result = new ConditionStatementFieldFromOneStream(internalID,
						fieldName, new Float(f), rel);
			} catch (NumberFormatException e) {
				ok = false;
			}

		} else if (type == event.DEFINES.CEPLEventDataFieldType.INT) {
			try {
				int i = Integer.parseInt(value);
				result = new ConditionStatementFieldFromOneStream(internalID,
						fieldName, new Integer(i), rel);
			} catch (NumberFormatException e) {
				ok = false;
			}

		} else if (type == event.DEFINES.CEPLEventDataFieldType.LONG) {
			try {
				long l = Long.parseLong(value);
				result = new ConditionStatementFieldFromOneStream(internalID,
						fieldName, new Long(l), rel);
			} catch (NumberFormatException e) {
				ok = false;
			}

		} else if (type == event.DEFINES.CEPLEventDataFieldType.SHORT) {
			try {
				short s = Short.parseShort(value);
				result = new ConditionStatementFieldFromOneStream(internalID,
						fieldName, new Short(s), rel);
			} catch (NumberFormatException e) {
				ok = false;
			}

		} else if (type == event.DEFINES.CEPLEventDataFieldType.TEXT) {
			// correct use of '
			if (value.indexOf('\'') != 0
					|| (value.indexOf('\'') == value.lastIndexOf('\''))
					|| value.lastIndexOf('\'') != value.length() - 1)
				ok = false;
			else {
				value = value.substring(value.indexOf('\'') + 1,
						value.lastIndexOf('\''));
				result = new ConditionStatementFieldFromOneStream(internalID,
						fieldName, new String(value), rel);
			}
		}

		if (!ok)
			throw new java.text.ParseException(
					"cstream :: Check the substatements! Invalid data value ("
							+ value + ")!", -1);
		else
			subCondition.addQueryStatement(result);
	}

	private void createNewFieldFromTwoStreams(long internalID1,
			String fieldName1, FieldRelationType relation, long internalID2,
			String fieldName2, SubCondition subCondition,
			StreamCondition streamCondition) throws java.text.ParseException {
		if (streamCondition.getExternalStreamID(internalID1) == null
				|| streamCondition.getExternalStreamID(internalID2) == null)
			throw new java.text.ParseException(
					"cstream :: Check the substatements! Unknown stream ID?",
					-1);

		Long externalID1 = streamCondition.getExternalStreamID(internalID1);
		event.DEFINES.CEPLEventDataFieldType type1 = EventFormatManager
				.instance().getFieldValueType(externalID1, fieldName1);
		Long externalID2 = streamCondition.getExternalStreamID(internalID2);
		event.DEFINES.CEPLEventDataFieldType type2 = EventFormatManager
				.instance().getFieldValueType(externalID2, fieldName2);

		if (type1 == event.DEFINES.CEPLEventDataFieldType.UNKNOWN
				|| type2 == event.DEFINES.CEPLEventDataFieldType.UNKNOWN)
			throw new java.text.ParseException(
					"cstream :: Check the substatements! Unknown data field?",
					-1);

		subCondition
				.addQueryStatement(new ConditionStatementFieldFromTwoStreams(
						internalID1, fieldName1, internalID2, fieldName2,
						relation));
	}
}